﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Compiler.TableOfSymbols.Types;
using Compiler.TableOfSymbols;

namespace Compiler.Unification.Substitutions
{
    public class Substitution
    {
        private NETTypesBijectiveMap typesBijectiveMap;

        private Dictionary<TypeVariable, BaseType> subs;

        public BaseType this[TypeVariable typeVar]
        {
            get { return subs[typeVar]; }
        }

        public bool IsEmpty
        {
            get { return subs.Count == 0; }
        }

        public Substitution(NETTypesBijectiveMap typesBijectiveMap)
        {
            if (typesBijectiveMap == null)
                throw new ArgumentNullException("typesBijectiveMap can not be null");
            subs = new Dictionary<TypeVariable, BaseType>();
            this.typesBijectiveMap = typesBijectiveMap;
        }

        public void ComposeWith(SubstitutionItem innerSubItem)
        {
            if (innerSubItem == null)
                throw new ArgumentNullException("innerSubItem can not be null");
            if (subs.ContainsKey(innerSubItem.FromVariable))
            {
                System.Diagnostics.Debug.Assert(false,
                    "В алгоритме унификации такой вариан не должен возникать");
            }
            else
            {
                // исправляем аргумент по известным данным
                innerSubItem = new SubstitutionItem(innerSubItem.FromVariable,
                    _ApplyAux(innerSubItem.ToType));
                // и теперь добавляем новый элемент
                BaseType toType = innerSubItem.ToType;
                if (toType.IsTypeVariable)
                {
                    TypeVariable toTypeVar = toType as TypeVariable;
                    if (subs.ContainsKey(toTypeVar))
                        toType = subs[toTypeVar];
                }
                subs.Add(innerSubItem.FromVariable, toType);
            }
        }

        public bool Contains(TypeVariable typeVar)
        {
            return subs.ContainsKey(typeVar);
        }

        public bool Contains(BaseType typeInfo)
        {
            if (!typeInfo.IsTypeVariable)
                return false;
            else
                return Contains(typeInfo as TypeVariable);
        }

        public BaseType Apply(BaseType typeInfo)
        {
            if (!Contains(typeInfo))
                return typeInfo;
            else
                return subs[typeInfo as TypeVariable];
        }

        private BaseType _ApplyAux(BaseType typeInfo)
        {
            //System.Diagnostics.Debug.Assert(!Contains(typeInfo), "Корректировка должна применяться только к сложным типам");
            // ???
            if (!Contains(typeInfo))
                foreach (KeyValuePair<TypeVariable, BaseType> subItem in this.subs)
                    typeInfo = typesBijectiveMap.TypeSubstitution(typeInfo, new SubstitutionItem(subItem.Key, subItem.Value));
            return typeInfo;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("[ ");
            foreach (KeyValuePair<TypeVariable, BaseType> pair in subs)
                sb.AppendFormat("{1}/{0}, ", 
                    typesBijectiveMap.GetTypeName(pair.Key), 
                    typesBijectiveMap.GetTypeName(pair.Value));
            if (!this.IsEmpty)
                sb = sb.Remove(sb.Length - 2, 2);
            sb.Append(" ]");
            return sb.ToString();
        }
    }
}
